package miniBanco.Mapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import basics.DBManager;
import basics.MySqlHelper;
import miniBanco.Core.DomainObject;
import miniBanco.Exceptions.HandledException;
import miniBanco.Mapper.Interface.IstatementSource;

public abstract class AbstractMapper {
	protected MapperRegistry mapperRegistry;
	protected MySqlHelper sqlHelper;
	private static final String TAG = AbstractMapper.class.getSimpleName();
	

	protected Map loadedMap = new HashMap();
	/** Metodos abstractos que la clase que hereda debe implementar **/
	abstract protected StringBuilder findStatement();
	/** Necesito que cada subTipo defina como hace el mapping **/
	abstract protected DomainObject doLoad(long id,ResultSet rs) throws SQLException;
	/** El subtipo tiene que definir el sql para el insert **/
	abstract protected StringBuilder doInsert(DomainObject subject);
	/** El subtipo tiene que definir el sql para el update **/
	abstract protected StringBuilder doUpdate(DomainObject subject);	
	/** El subtipo define el nombre de la tabla asi armo el select a mano **/ 
	abstract protected String getTableName();
	/** Metodo para persistir la informacion **/
	public abstract void doSave(DomainObject subject);	
	//Solo para ver que funcionen los select dps hay que borrarlo
	public void borrarLoadedMap()
	{
		loadedMap.clear();
	}
	AbstractMapper()
	{
		mapperRegistry = MapperRegistry.GetInstance();
		sqlHelper = MySqlHelper.getInstance();
	}

	public long Count()
	{
		StringBuilder sb = new StringBuilder();
		sb.append("SELECT COUNT(*) FROM "+getTableName());
		String sql = sb.toString();
		ResultSet rs = sqlHelper.ExecuteQuery(sql, TAG);
		try {
			rs.next();
			return rs.getLong(1);
		} catch (Exception ex) {
			DBManager.Error(ex);
			throw new HandledException("Se produjo un error inesperado. Ver log. Error="+ex.getMessage());
		}
	}
		
	protected DomainObject abstractFind(long id)
	{
		DomainObject result = (DomainObject) loadedMap.get(id);
		if(result != null) return result;
		StringBuilder findStatement = null;
		findStatement = findStatement();
		String sql = String.format(findStatement.toString(), id);
		ResultSet rs = sqlHelper.ExecuteQuery(sql, TAG);
		try {
			rs.next();
			result = load(rs);
		}
		catch(Exception ex1)
		{
			DBManager.Error(ex1);
			throw new HandledException("Se produjo un error inesperado. Ver log. Error="+ex1.getMessage());
		}
		return result;
	}
	protected DomainObject load(ResultSet rs) throws SQLException
	{
		long id = rs.getLong(1);
		if(loadedMap.containsKey(id))
			return (DomainObject) loadedMap.get(id);
		DomainObject result = doLoad(id,rs);
		//result.setID(id);
		loadedMap.put(id,result);
		result.isDirty = false;
		return result;
	}
	/** Levanto todo el resultset y lo dejo en el lista.
	 * Llamo el metodo load de cada subtipo para que haga el mapping correspondiente **/
	protected List loadAll(ResultSet rs) throws SQLException
	{
		List result = new ArrayList();
		while(rs.next())
			result.add(load(rs));
		return result;
	}
	/** Paso la connection para hacer el rollback ojo que no hace el close ni el commit**/
	protected long save(DomainObject subject)
	{
		//me fijo si lo tengo cargado o si fue modificado sino no hago nada
		if(loadedMap.get(subject.getID()) == null)
		{
			StringBuilder insertStatement = doInsert(subject);
			sqlHelper.ExecuteNonQuery(insertStatement.toString(), TAG);
			loadedMap.put(subject.getID(),subject);
			//Aca tengo un hermoso bug...Cuando di de alta la entidad cambio el ID
			//deberia buscar el nuevo Id y setearlo
			return subject.getID(); 
		}
		if(subject.isDirty)
		{
			StringBuilder updateStatement = doUpdate(subject);
			loadedMap.remove(subject.getID());
			sqlHelper.ExecuteNonQuery(updateStatement.toString(), TAG);
			loadedMap.put(subject.getID(),subject);
			subject.isDirty = false;
			return subject.getID(); 
		}
		return subject.getID();
	}
	protected void delete(DomainObject subject)
	{
		long id = subject.getID();
		String deleteStatement = "Delete from "+getTableName()+" where id="+ subject.getID();
		sqlHelper.ExecuteNonQuery(deleteStatement, TAG);
		loadedMap.remove(id);
	}
	
	/** Metodo generico que recibe IstatementeSource.
	 *  Del IstatemetSource recibo el sql a ejecutar y los parametros 
	 *  y aca los reeplazo
	 *  **/
	public List findMany(IstatementSource source)
	{
		String sql = null;
		ResultSet rs = null;
		sqlHelper = MySqlHelper.getInstance();	
		sql = source.sql();
		for(int i= 0;i < source.parameters().length; i++)
		{
			sql = String.format(sql, source.parameters()[i]);
		}
		rs = sqlHelper.ExecuteQuery(sql, TAG);
		try {
			return loadAll(rs);
		}
		catch(Exception ex1)
		{
			DBManager.Error(ex1);
			throw new HandledException("Se produjo un error inesperado. Ver log. Error = "+ex1.getMessage());
		}
	}

}
